﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace Think.Core.DynamicOpr
{
    /// <summary>
    /// 成员调用器
    /// </summary>
    public class MemberInvoker : IDynamicInvoker
    {
        static ConcurrentDictionary<MemberInfo, IDynamicInvoker> Cache = new ConcurrentDictionary<MemberInfo, IDynamicInvoker>();
        
        /// <summary>
        /// 获取调用器
        /// </summary>
        /// <param name="mi"></param>
        /// <returns></returns>
        public static IDynamicInvoker GetInvoker(MemberInfo mi)
        {
            IDynamicInvoker ma = null;
            if (Cache.TryGetValue(mi, out ma))
            {
                return ma;
            }
            if (mi.MemberType == MemberTypes.Method)
            {
                ma = new MethodInvoker(mi as MethodInfo);
            }
            else if (mi is PropertyInfo)
            {
                ma = new PropertyInvoker(mi as PropertyInfo);
            }
            else
            {
                ma = new ConstructorInvoker(mi as ConstructorInfo);
            }
            Cache.TryAdd(mi, ma);
            return ma;
        }
        
        /// <summary>
        /// 调用成员函数
        /// </summary>
        /// <param name="mi"></param>
        /// <param name="instance"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static object Invoke(MemberInfo mi, object instance, params object[] args)
        {
            return GetInvoker(mi).Invoke(instance, args);
        }

        /// <summary>
        /// 执行成员调用
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="parameters"></param>
        /// <returns>object||VoidResult</returns>
        public object Invoke(object obj, params object[] parameters)
        {
            return OverwriteInvoker(obj, parameters);
            //  return Contants.Void;
        }
        
        /// <summary>
        /// 初始化调用器
        /// </summary>
        public virtual void InitInvoker()
        {
            throw new NotImplementedException();
        }
        
        /// <summary>
        /// 复写调用逻辑
        /// </summary>
        public Func<object, object[], object> OverwriteInvoker
        {
            get;
            set;
        }
        /// <summary>
        /// 成员信息
        /// </summary>
        public MemberInfo MemberInfo
        {
            get;
            set;
        }

        /// <summary>
        /// 方法调用
        /// </summary>
        public class MethodInvoker : IDynamicInvoker
        {
            /// <summary>
            /// 构造
            /// </summary>
            /// <param name="info"></param>
            public MethodInvoker(MethodInfo info)
            {
                _methodInfo = info;
                InitInvoker();
            }

            MethodInfo _methodInfo = null;
            #region IDynamicInvoker 成员
            /// <summary>
            /// 执行调用
            /// </summary>
            /// <param name="obj"></param>
            /// <param name="parameters"></param>
            /// <returns></returns>
            public object Invoke(object obj, params object[] parameters)
            {
                return OverwriteInvoker(obj, parameters);
            }
            /// <summary>
            /// 初始化
            /// </summary>
            public void InitInvoker()
            {
                var instance = Expression.Parameter(typeof(object), "instance");

                var parms = Expression.Parameter(typeof(object[]), "args");

                var paramsInfos = _methodInfo.GetParameters();
                var paramsList = new List<Expression>(paramsInfos.Length);
                for (int i = 0; i < paramsInfos.Length; i++)
                {
                    var value = Expression.ArrayIndex(parms, Expression.Constant(i));
                    //MethodInfo changeTypeMethod = typeof(System.Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) });
                    //Expression convertedExprAsObject = Expression.Call(changeTypeMethod, value, Expression.Constant( paramsInfos[i].ParameterType));
                    var cvalue = Expression.Convert(value, paramsInfos[i].ParameterType);
                    paramsList.Add(cvalue);
                }
                var cinstance = _methodInfo.IsStatic ? null : Expression.Convert(instance, _methodInfo.ReflectedType);
                var call = Expression.Call(cinstance, _methodInfo, paramsList);
                if (call.Type == typeof(void))
                {
                    var lambda = Expression.Lambda<Action<object, object[]>>(call, instance, parms);
                    Action<object, object[]> act = lambda.Compile();
                    this.OverwriteInvoker = (ins, args) =>
                    {
                        act(ins, args);
                        return Constants.Void;
                    };
                }
                else
                {
                    var ret = Expression.Convert(call, typeof(object));
                    var lambda = Expression.Lambda<Func<object, object[], object>>(
                       ret, instance, parms);

                    this.OverwriteInvoker = lambda.Compile();
                }
            }
            /// <summary>
            /// 复写调用逻辑
            /// </summary>
            public Func<object, object[], object> OverwriteInvoker
            {
                get;
                set;
            }
            /// <summary>
            /// 成员信息
            /// </summary>
            public MemberInfo MemberInfo
            {
                get
                {
                    return _methodInfo;
                }
                set
                {
                    _methodInfo = value as MethodInfo;
                }
            }

            #endregion
        }
        /// <summary>
        /// 属性调用
        /// </summary>
        public class PropertyInvoker : IDynamicInvoker
        {
            PropertyInfo _propertyInfo = null;
            /// <summary>
            /// Ctor
            /// </summary>
            /// <param name="info"></param>
            public PropertyInvoker(PropertyInfo info)
            {
                _propertyInfo = info;
                InitInvoker();
            }

            /// <summary>
            /// 调用
            /// </summary>
            /// <param name="obj"></param>
            /// <param name="parameters"></param>
            /// <returns></returns>
            public object Invoke(object obj, params object[] parameters)
            {
                return OverwriteInvoker(obj, parameters);
            }
            Action<object, object> _setter = null;
            Func<object, object[], object> _getter = null;
            /// <summary>
            /// 复写调用
            /// </summary>
            public Func<object, object[], object> OverwriteInvoker
            {
                get;
                set;
            }
            /// <summary>
            /// 成员
            /// </summary>
            public MemberInfo MemberInfo
            {
                get
                {
                    return _propertyInfo;
                }
                set
                {
                    _propertyInfo = value as PropertyInfo;
                }
            }

            void setGetter()
            {
                if (!_propertyInfo.CanRead)
                {
                    return;
                }
                var instance = Expression.Parameter(typeof(object), "instance");
                var cinstance = Expression.Convert(instance, _propertyInfo.GetGetMethod(true).IsStatic ? null : _propertyInfo.ReflectedType);
                var propvalue = Expression.Property(cinstance, _propertyInfo);
                var cpropValue = Expression.Convert(propvalue, typeof(object));
                var lambda = Expression.Lambda<Func<object, object>>(cpropValue, instance);
                var func = lambda.Compile();
                _getter = (obj, arr) => func(obj);
            }

            void setSetter()
            {
                if (_propertyInfo.CanWrite)
                {
                    var instance = Expression.Parameter(typeof(object), "instance");
                    var cinstance = Expression.Convert(instance, _propertyInfo.GetSetMethod(true).IsStatic ? null : _propertyInfo.ReflectedType);
                    var cvalue = Expression.Parameter(typeof(object), "arg");
                    MethodInfo changeTypeMethod = typeof(Think.Extend.TypeExtend).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) });
                    Expression convertedExprAsObject = Expression.Convert(Expression.Call(changeTypeMethod, cvalue, Expression.Constant(_propertyInfo.PropertyType)), _propertyInfo.PropertyType);
                    var propvalue = Expression.Property(cinstance, _propertyInfo);
                    var ass = Expression.Assign(propvalue, convertedExprAsObject);
                    var lambda = Expression.Lambda<Action<object, object>>(ass, instance, cvalue);
                    this._setter = lambda.Compile();
                }
            }


            /// <summary>
            /// 初始化
            /// </summary>
            public void InitInvoker()
            {
                setSetter();
                //setterIL();
                setGetter();
                OverwriteInvoker = (obj, arr) =>
                {
                    if (arr == null || arr.Length == 0)
                    {
                        return _getter(obj, arr);
                    }
                    _setter(obj, arr[0]);
                    return obj;
                };
            }

        }

        /// <summary>
        /// 构造函数调用
        /// </summary>
        public class ConstructorInvoker : IDynamicInvoker
        {

            ConstructorInfo _constructorInfo = null;
            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="info"></param>
            public ConstructorInvoker(ConstructorInfo info)
            {
                _constructorInfo = info;
                InitInvoker();
            }
            #region IDynamicInvoker 成员
            /// <summary>
            /// 调用构造函数
            /// </summary>
            /// <param name="obj"></param>
            /// <param name="parameters"></param>
            /// <returns>实例</returns>
            public object Invoke(object obj, params object[] parameters)
            {
                return OverwriteInvoker(obj, parameters);
            }
            /// <summary>
            /// 复写调用
            /// </summary>
            public Func<object, object[], object> OverwriteInvoker
            {
                get;
                set;
            }
            /// <summary>
            /// 成员信息 ConstructorInfo
            /// </summary>
            public MemberInfo MemberInfo
            {
                get
                {
                    return _constructorInfo;
                }
                set
                {
                    _constructorInfo = value as ConstructorInfo;
                }
            }
            /// <summary>
            /// 设置构造函数调用方法，OverwriteInvoker
            /// </summary>
            public void InitInvoker()
            {
                var consParams = Expression.Parameter(typeof(object[]), "parameters");
                var paramArr = _constructorInfo.GetParameters();
                var paramsList = new List<Expression>(paramArr.Length);

                for (int i = 0; i < paramArr.Length; i++)
                {
                    var value = Expression.ArrayIndex(consParams, Expression.Constant(i));
                    var cvalue = Expression.Convert(value, paramArr[i].ParameterType);
                    paramsList.Add(cvalue);
                }
                var instance = Expression.New(_constructorInfo, paramsList);
                var cinstance = Expression.Convert(instance, typeof(object));
                var lambda = Expression.Lambda<Func<object[], object>>(cinstance, consParams);
                var func = lambda.Compile();
                this.OverwriteInvoker = (obj, arr) => { return func(arr); };
            }
            #endregion
        }
    }
}
